"""
音频处理模块
负责录音、播放音频
"""
import sounddevice as sd
import numpy as np
import queue
import threading
import time
from typing import Callable


class AudioManager:
    """音频管理器"""
    
    def __init__(self, sample_rate: int = 16000, channels: int = 1):
        self.sample_rate = sample_rate
        self.channels = channels
        self.recording = False
        self.playing = False
        
        # 录音缓冲
        self.record_buffer = []
        self.record_stream = None
        
        # 播放队列（限制大小，避免积压）
        self.play_queue = queue.Queue(maxsize=50)
        self.play_thread = None
        self.play_stream = None
        self.play_lock = threading.Lock()  # 播放线程锁
        
        # 实时发送队列（避免阻塞音频线程）
        self.send_queue = queue.Queue(maxsize=100)
        self.send_thread = None
        self.send_running = False
        
        # 回调
        self.on_recording_started: Callable = None
        self.on_recording_stopped: Callable[[bytes], None] = None
        self.on_audio_chunk: Callable[[bytes], None] = None  # 实时音频块回调
        
        # 日志计数器（减少日志输出）
        self.play_log_counter = 0
        self.receive_log_counter = 0
        
        # 启动发送线程
        self._start_send_thread()
    
    def start_recording(self):
        """开始录音"""
        print("[AudioManager] ====== start_recording() 被调用 ======")
        
        if self.recording:
            print("[AudioManager] 已经在录音状态，忽略")
            return
        
        print("[AudioManager] 设置 recording = True")
        self.recording = True
        self.record_buffer = []
        
        try:
            print("[AudioManager] 创建音频输入流...")
            self.record_stream = sd.InputStream(
                samplerate=self.sample_rate,
                channels=self.channels,
                dtype='int16',
                callback=self._record_callback
            )
            self.record_stream.start()
            print("[AudioManager] 音频输入流已启动")
            
            if self.on_recording_started:
                print("[AudioManager] *** 调用 on_recording_started 回调 ***")
                self.on_recording_started()
                print("[AudioManager] on_recording_started 回调已执行")
            else:
                print("[AudioManager] 警告: on_recording_started 回调未设置")
        except Exception as e:
            print(f"[AudioManager] 错误：开始录音失败: {e}")
            self.recording = False
    
    def stop_recording(self):
        """停止录音"""
        print("[AudioManager] ====== stop_recording() 被调用 ======")
        
        if not self.recording:
            print("[AudioManager] 未在录音状态，返回空数据")
            return b""
        
        print("[AudioManager] 设置 recording = False")
        self.recording = False
        
        try:
            if self.record_stream:
                print("[AudioManager] 停止并关闭音频流...")
                self.record_stream.stop()
                self.record_stream.close()
                self.record_stream = None
                print("[AudioManager] 音频流已关闭")
            
            # 将录音数据合并
            if self.record_buffer:
                print(f"[AudioManager] 合并录音数据，缓冲区数量: {len(self.record_buffer)}")
                audio_data = np.concatenate(self.record_buffer, axis=0)
                audio_bytes = audio_data.tobytes()
                print(f"[AudioManager] 录音数据大小: {len(audio_bytes)} 字节")
                
                if self.on_recording_stopped:
                    print("[AudioManager] *** 调用 on_recording_stopped 回调 ***")
                    self.on_recording_stopped(audio_bytes)
                    print("[AudioManager] on_recording_stopped 回调已执行")
                else:
                    print("[AudioManager] 警告: on_recording_stopped 回调未设置")
                
                return audio_bytes
            else:
                print("[AudioManager] 警告：录音缓冲区为空")
                return b""
        except Exception as e:
            print(f"[AudioManager] 错误：停止录音失败: {e}")
            import traceback
            traceback.print_exc()
        
        return b""
    
    def _start_send_thread(self):
        """启动异步发送线程"""
        if not self.send_running:
            self.send_running = True
            self.send_thread = threading.Thread(target=self._send_loop, daemon=True)
            self.send_thread.start()
            print("[AudioManager] 异步发送线程已启动")
    
    def _send_loop(self):
        """发送循环（在独立线程中运行）"""
        print("[AudioManager] 发送线程开始运行")
        while self.send_running:
            try:
                # 从队列获取音频块（超时1秒）
                audio_chunk = self.send_queue.get(timeout=1.0)
                
                # 调用回调发送音频
                if self.on_audio_chunk:
                    print(f"[AudioManager] 发送线程：调用回调发送 {len(audio_chunk)} 字节")
                    self.on_audio_chunk(audio_chunk)
                else:
                    print(f"[AudioManager] 警告：on_audio_chunk回调未设置")
                    
            except queue.Empty:
                continue
            except Exception as e:
                print(f"[AudioManager] 发送线程错误: {e}")
                import traceback
                traceback.print_exc()
        
        print("[AudioManager] 发送线程已退出")
    
    def _record_callback(self, indata, frames, time_info, status):
        """录音回调（在音频线程中执行，必须快速返回）"""
        if status:
            print(f"录音状态: {status}")
        
        if self.recording:
            # 保存到缓冲区（用于最后的完整音频）
            self.record_buffer.append(indata.copy())
            
            # 将音频块放入发送队列（非阻塞）
            # 不直接调用回调，避免阻塞音频线程
            try:
                audio_bytes = indata.tobytes()
                self.send_queue.put_nowait(audio_bytes)  # 非阻塞放入
            except queue.Full:
                # 队列满了，丢弃这一帧（避免阻塞）
                print("[AudioManager] 发送队列已满，丢弃音频帧")
                pass
    
    def play_audio(self, audio_data: bytes):
        """播放音频数据（优化版，减少阻塞）"""
        try:
            # 将字节数据转换为numpy数组
            audio_array = np.frombuffer(audio_data, dtype='int16')
            
            # 减少日志输出（每20次输出一次）
            self.receive_log_counter += 1
            if self.receive_log_counter % 20 == 0:
                print(f"[AudioManager] 接收音频 #{self.receive_log_counter}, 队列大小: {self.play_queue.qsize()}")
            
            # 添加到播放队列（非阻塞，避免卡死）
            try:
                self.play_queue.put_nowait(audio_array)
            except queue.Full:
                # 队列满了，快速丢弃一些旧数据（不要全部清理，避免卡顿）
                discarded = 0
                for _ in range(10):  # 只清理10个，快速释放空间
                    try:
                        self.play_queue.get_nowait()
                        discarded += 1
                    except queue.Empty:
                        break
                
                if discarded > 0 and self.receive_log_counter % 20 == 0:
                    print(f"[AudioManager] 播放队列满，已丢弃 {discarded} 个旧音频块")
                
                # 再次尝试放入（不阻塞）
                try:
                    self.play_queue.put_nowait(audio_array)
                except queue.Full:
                    pass  # 仍然满，直接丢弃这一帧，避免阻塞
            
            # 如果播放线程未启动，启动它（使用锁保护）
            if not self.playing:
                with self.play_lock:
                    if not self.playing:  # 双重检查
                        self._start_playback()
        except Exception as e:
            # 减少错误日志输出频率
            if self.receive_log_counter % 50 == 0:
                print(f"[AudioManager] 播放音频失败: {e}")
    
    def _start_playback(self):
        """启动播放线程"""
        if self.playing:
            print("[AudioManager] 播放线程已在运行，跳过启动")
            return
        
        print("[AudioManager] 启动播放线程...")
        self.playing = True
        self.play_thread = threading.Thread(target=self._playback_loop, daemon=True)
        self.play_thread.start()
        print("[AudioManager] 播放线程已启动")
    
    def _playback_loop(self):
        """播放循环（优化版，减少日志输出）"""
        print("[AudioManager] 播放循环开始运行")
        stream = None
        try:
            print("[AudioManager] 正在创建音频输出流...")
            stream = sd.OutputStream(
                samplerate=self.sample_rate,
                channels=self.channels,
                dtype='int16',
                blocksize=2048  # 设置合理的blocksize，避免延迟
            )
            stream.start()
            print(f"[AudioManager] 音频输出流已创建，采样率: {self.sample_rate}")
            
            audio_count = 0
            idle_count = 0  # 空闲计数，用于自动退出
            
            while self.playing:
                try:
                    # 短超时，快速响应停止信号
                    audio_data = self.play_queue.get(timeout=0.5)
                    audio_count += 1
                    idle_count = 0  # 重置空闲计数
                    
                    # 减少日志输出（每50个音频块输出一次）
                    self.play_log_counter += 1
                    if self.play_log_counter % 50 == 0:
                        print(f"[AudioManager] 播放进度: {audio_count} 块，队列: {self.play_queue.qsize()}")
                    
                    # 播放音频（非阻塞写入）
                    stream.write(audio_data)
                    
                except queue.Empty:
                    idle_count += 1
                    # 如果连续空闲超过10次（5秒），且不再播放，则退出
                    if idle_count > 10 and not self.playing:
                        print("[AudioManager] 播放队列长时间空闲，退出播放循环")
                        break
                    continue
                except Exception as e:
                    # 减少错误日志频率
                    if audio_count % 50 == 0:
                        print(f"[AudioManager] 播放音频块错误: {e}")
            
            print(f"[AudioManager] 播放循环结束，共播放 {audio_count} 个音频块")
        except Exception as e:
            print(f"[AudioManager] 创建/使用播放流失败: {e}")
            import traceback
            traceback.print_exc()
        finally:
            # 确保流被关闭
            if stream:
                try:
                    stream.stop()
                    stream.close()
                except:
                    pass
            self.playing = False
            print("[AudioManager] 播放线程已退出")
    
    def stop_playback(self):
        """停止播放"""
        print("[AudioManager] 停止播放...")
        self.playing = False
        
        # 清空播放队列
        cleared_count = 0
        while not self.play_queue.empty():
            try:
                self.play_queue.get_nowait()
                cleared_count += 1
            except queue.Empty:
                break
        
        if cleared_count > 0:
            print(f"[AudioManager] 已清空播放队列，丢弃 {cleared_count} 个音频块")
        
        # 等待播放线程退出
        if self.play_thread and self.play_thread.is_alive():
            print("[AudioManager] 等待播放线程退出...")
            self.play_thread.join(timeout=2.0)
            if self.play_thread.is_alive():
                print("[AudioManager] 警告：播放线程未在2秒内退出")
            else:
                print("[AudioManager] 播放线程已退出")
        
        print("[AudioManager] 播放已停止")
    
    def cleanup(self):
        """清理资源"""
        print("[AudioManager] 开始清理资源...")
        
        try:
            # 先停止录音和播放
            self.stop_recording()
            print("[AudioManager] 录音已停止")
        except Exception as e:
            print(f"[AudioManager] 停止录音异常: {e}")
        
        try:
            self.stop_playback()
            print("[AudioManager] 播放已停止")
        except Exception as e:
            print(f"[AudioManager] 停止播放异常: {e}")
        
        try:
            # 停止发送线程
            print("[AudioManager] 正在停止发送线程...")
            self.send_running = False
            if self.send_thread and self.send_thread.is_alive():
                self.send_thread.join(timeout=3.0)
                if self.send_thread.is_alive():
                    print("[AudioManager] 警告：发送线程未能在3秒内退出")
                else:
                    print("[AudioManager] 发送线程已成功退出")
        except Exception as e:
            print(f"[AudioManager] 停止发送线程异常: {e}")
        
        try:
            # 关闭音频流
            if self.record_stream:
                self.record_stream.close()
                self.record_stream = None
                print("[AudioManager] 录音流已关闭")
        except Exception as e:
            print(f"[AudioManager] 关闭录音流异常: {e}")
        
        try:
            if self.play_stream:
                self.play_stream.close()
                self.play_stream = None
                print("[AudioManager] 播放流已关闭")
        except Exception as e:
            print(f"[AudioManager] 关闭播放流异常: {e}")
        
        print("[AudioManager] 资源清理完成")

